TwistedPixel Preset Development Guide
v1.0
Copyright © 2003 Brian Spangler, All rights reserved.
http://www.outdoorgrace.com
*portions of this document are excerpts
included with permission from
the Beginners Guide to MilkDrop Preset Writing developed by
John Rovastar at
www.milkdrop.co.uk
Usage
Introduction
Requirements
Installing
Keyboard Shortcuts
Troubleshooting
Acknowledgements
Presets
Scripting
A Preset's Lifetime
Color Cycling
Variables -- A Basic Tutorial
Per Frame Equations
Per Pixel Equations
Function Reference
Custom Waveforms
Custom Shapes
Conversion Utility
Debugging With Microsoft's Script Editor
TwistedPixel is a WinAMP and WMP Visualization that uses a video card's 3d capabilities and Microsoft's Direct3D to produce flowing images in reaction to music. How TwistedPixel differs from other visualizations is that there exists no hardcoded visuals, all the imagery produced is done so via the interpretation of presets files that anyone can write. A few hundred preset files have been included with the installation of TwistedPixel so as to allow immediate viewing of the visualization. Feel free to write your own and share them with the preset authoring community.
A little history, the first visualization that I encountered that offered the
use of presets was NullSoft's Milkdrop. At that time I had just released
PixelTrip and knew immediately I was already behind the pack of visualization
development. Since that time I have worked periodically on TwistedPixel until it
had reached the same levels of Milkdrop and even supported Milkdrop preset
files. Initially the entire vis was developed using C++ and MMX so as to be able
to process the necessary time variable ... although the vis looked quite
similar to Milkdrop it lacked the performance and levels of color and alpha
blending. It was Roman Komary and his
excellent vis X-Plugin that turned my
attention to D3D and the advantages of its use; since that time TwistedPixel has
been redeveloped with D3D. Once the project was completed I did a very unusual
thing...I shelved it.
With Milkdrop, at the time, in its 1.3 release I saw no reason to put out what
was basically a clone that I had developed to see if I could and that offered
very little functionality over the original. Since then I have been involved in
developing projects that utilize varying scripting technologies and for the
first time I saw something that could be added to TwistedPixel that would
provide an appeal over Milkdrop -- you can now develop your presets using
VBScript, JavaScript, TCL, Pearl and Python. By adding support for
full blown scripting languages the door is open to a whole new breed of
visualization presets. To help existing preset authors port Milkdrop presets to
VBScript or JavaScript there is included in the installation a Milkdrop To
TwistedPixel converter that works on a single preset or an entire folder of
presets.
In time a formal installer will be developed
for TwistedPixel, the TwistedPixel SDK and all the supported presets to date.
For now there is little more than a zip file containing the files and the
directory structure necessary to run on WinAMP or WMP.
If you are a WinAMP user and have it installed in your ..\Program Files\ folder
then unzipping to the WinAMP folder will be enough to establish the folder
hierarchy necessary.
If you are a WMP user then you can unzip the files to the WMP visualizations
folder and then register the component using the utility regsvr32 ... e.g.
regsvr23 TwistedPixel.dll
The folder hierarchy used in release 1.0 of TwistedPixel follows (where <> denotes a folder) :
<WinAMP>
<PlugIns>
vis_tp.dll
<TwistedPixel>
preset files...
<TwistedPixel
SDK>
'this guide'.html
MD2TP.exe
<Windows Media Player>
<Visualizations>
TwistedPixel.dll
<TwistedPixel>
preset files...
<TwistedPixel
SDK>
'this guide'.html
MD2TP.exe
Once installed, run WinAMP and press Ctrl-K or run WMP and use the menus to select configure. On configure, the TwistedPixel configuration window dialog will be presented for you to make any selections beyond the defaults.
While TwistedPixel is running you can use
keyboard shortcuts to change behaviors of the visualization.
Keyboard Shortcut f toggle display of Frames Per Second
Keyboard Shortcut d toggle beat detection
Keyboard Shortcut p display name of current preset
Keyboard Shortcut n load next preset
Keyboard Shortcut o open a specific preset
Keyboard Shortcut r toggle random selection of preset
Keyboard Shortcut i flag the current preset as incomplete
A note about shortcut key i:
If you are converting many Milkdrop presets to VBScript or JavaScript using the
MD2TP.exe utility then this shortcut feature is a quick way to flag those
presets that did not convert as expected. The basic idea is to convert a folder
of Milkdrop presets where the output folder is an empty TwistedPixel folder
(review folder hierarchy above). Once the newly
converted presets are populating the TwistedPixel folder, launch WinAMP or WMP
and begin to proof the conversions. Use the 'n' key to move forward through the
list of presets and use 'i' to flag the ones that failed conversion. Pressing 'i'
will rename the preset file with the prefix 'incomplete.conversion.' which is
very easy to identify in Windows Explorer.
The following are suggestions you can take in
the event of a visualization failure.
First determine if the problem you are experiencing is a problem in TwistedPixel
or in visualizations and video in general. An easy approach to doing this is to
simply try a different Direct3D visualization, if it works then the problem you
are experiencing is most likely an issue with TwistedPixel and so you should
contact me with a description of the issue. Please do not be offended if I do
not get back to you in a quick manner -- I receive too many emails in a given
day to read/respond to them all.
If you have determined that there is likely a problem with your video then try
any of the following:
- Ensure your video card supports Direct3D -- a number of reported problems are were just a matter of a video card with a lack of support for basic 3D hardware acceleration.
- Get the latest DirectX drivers from Microsoft and/or re-install what the current version. There is the possibility that DirectX runtime files are failing in which case a fresh installation will reset appropriate information at the file system level and Windows Registry database.
- Get the latest drivers from your video card manufacturer. Video Card manufacturers are always updating their drivers to be more compliant with the latest releases of DirectX and OpenGL standards.
- Revert back to a previous set of video drivers. If you have just upgraded the drivers and find applications failing with video errors then there is the possibility the newest drivers have introduced the problems. This is a regression issue and should be reported to your video card manufacturer so that they can develop a patch to that driver release version.
There are few to whom I owe some thanx that if not for there help I would never have completed Twisted Pixel. To Dave Wolfe, a thanx in testing and providing feedback. To Roman Komary who turned the light on for me regarding Direct3D and who answered many questions about the uses of Milkdrop variables. To Rovastar for the influence in completing this work and for keeping me informed about the vis community. And of course a thank you to Ryan Geiss whose original development of Milkdrop inspired me to try and write one too.
As already mentioned, one of the greatest assets to the TwistedPixel visualization is its ability to support preset files written in VBScript, JavaScript, Python, Pearl and TCL. To determine the scripting language in use for a given preset TwistedPixel will use the file extensions .vbs, .js, .py, .tcl and .pls to load the appropriate support. By default the Windows installs the scripting language runtime support for VBScript and JavaScript, for Python, Pearl and TCL you will need to install their scripting language runtime files which are available for download from a number of web sites.
Four things are useful for writing presets:
Mathematics knowledge, artistic flare, persistence and luck. If you have any of
these you will be able to create a basic preset and with greater capabilities of
course comes more elaborate presets.
When it comes to the basic mathematics knowledge you can find a number of sites
that delve into the uses of sin, cos and tan as well as coordinate systems
cartesian and polar.
You can look at existing presets and randomly copy things into your own to view
the results and possibly get a preset you like, but if you actually understand
what is happening with regards to the mathematics then you will be able to
elaborate on the preset code and adjust to produce the results you desire. In
time I intend to release a UI development environment that allows you to
graphically build a preset and which will generate the necessary code for you.
A quick note about the scripting editor I
use...
Although notepad is doable I prefer features such as syntax coloring and code
completion etc. After having looked at a dozen or so script editors for VBScript
and JavaScript I came upon one that I really like and that is built into
Microsoft Office titled 'Microsoft Script Editor'. If you install Microsoft
Office you will probably not find any mention or link to the editor anywhere
(why? I am not sure). If you used all the default settings during the
installation then you can find the script editor at "C:\Program
Files\Microsoft Office\Office10\MSE7.EXE"; from Window's Explorer right
click on the file and click on 'Create Desktop Shortcut'.
During a preset's lifetime the TwistedPixel visualization will invoke a series of methods that are available in your preset. It is in the body of these methods that you develop your code the ultimately alters the behaviors of the pixels on the screen in response to sound. Only two of these methods are required, the remaining methods are optional and will only be invoked by TwistedPixel when found at the time of loading and evaluating the preset. The methods are as follows in VBScript syntax, syntax will differ depending on your scripting language:
| Sub PresetInitialize() | Called when the preset is
first loaded. Required. It is in this method that all variables should be initialized including those specific to TwistedPixel |
| Sub ProcessPerFrame() | Called once for each frame
that is rendered to the screen. Required. |
| Sub ProcessPerPixel() | Called once for each pixel
that makes up a frame. Optional. |
| Function ProcessWaveform() | Called once per frame when
TwistedPixel is rendering waveforms to the screen. In the body of this method you call the TwistedPixel function AddWaveformElement for each element of a waveform you want to add to the screen (more about this later). Returning True to TwistedPixel is an indicator that you want to build another waveform and so TwistedPixel will render the waveform you built and call ProcessWaveform for the next preset waveform. Returning False to TwistedPixel is an indicator that no more waveforms will be added to this frame. |
| Function ProcessCustomShape() | Called once per frame when
TwistedPixel is rendering custom shapes to the screen. In the body of this method you set preset variables specific to shapes (more about this later) that are to be displayed on the screen. Returning True to TwistedPixel is an indicator that you want to add more shapes and so TwistedPixel will render the current shape and call ProcessCustomShape for the next preset shape. Returning False to TwistedPixel is an indicator that no more shapes will be added to this frame. |
| Sub PresetException(ByVal eCode, ByRef eDescription) | Called when a script error has
occurred. In addition the script error will be presented in the lower left
hand corner of the screen. |
| Sub PresetTerminate() | Called when the preset is
unloading. Used for clean up of any resources your preset allocated.
|
One of the first things people want to do is to have different colors on the screen, that is via waveforms, borders, custom shapes etc. The simplest way to do this is add some simple formulas to the ProcessPerFrame method. Now, let's say you wanted to make the color of the waveform, sound wave that gets plotted on the screen, vary through time. Color, relative to Windows, is defined by three components, these are red, green, and blue; each is in the range 0 to 1 where 0 is empty and 1 is full intensity. Adding the following to the ProcessPerFrame method (in JavaScript):
tp_wave_r += 0.5 * Math.sin(tp_time*1.13);
tp_wave_g += 0.5 * Math.sin(tp_time*1.23);
tp_wave_b += 0.5 * Math.sin(tp_time*1.33);
Now once per rendered frame the three color
components will change or cycle in time at an amplitude between .5 and 1.0 and
at a frequency of either 1.13, 1.23 or 1.33 -- by using varying frequencies you
ensure that the cycling of each component occurs a different times which
produces a broader range of color. If you were to use the same frequency for all
then only one color level, gray, would be recognized.
Sine (and cosine) waves have a range of -1 to 1 so if to use these functions
within an allowable range of 0 thru 1 you will have to modify them.
0.5 + 0.5 * Math.Sin(tp_time) = 0.5 + (-0.5 to 0.5) = 0 to 1;
This will generate the range 0 to 1 and then 1
to 0, oscillation, over a period of 6.28 seconds (the value of PI*2) for one
complete cycle. If you want to oscillate or cycle at a different time interval
then multiplying the TwistedPixel time variable, tp_time, will change the 6.28
period. For example the following 0.5 * sin(2 * tp_time) will produce a time
period is now 6.28/2 = 3.14 seconds which is a shorter time interval that causes
the cycling to appear faster. To slow down the cycling you would divide vs.
multiply.
If you want the color variables to be focused on a stricter range of color, lets
say red, then you could alter the equation as follows 0.75 + 0.25 *
Math.Sin(tp_time) = 0.75 + (-0.25 to 0.25) = 0.5 to 1. This limits the red
component to upper values of .75 thru 1.0.
Also you can base the color output on the TwistedPixel sound variables tp_bass,
tp_treb_att, etc. to create waveform colors in reaction to what the music is
doing.
There are an endless number of effects that can
be produced with TwistedPixel. The most minor changes to any one of the
TwistedPixel variables can change the entire look of a preset which is generally
the most common way of developing new presets.
TwistedPixel variables are those values you see in the script that are prefixed
with the values 'tp_' -- although MilkDrop variables do not have this prefix it
is very easy to add the prefix if you are porting a MilkDrop preset since the
remaining part of the value is identical to those supported in MilkDrop. For
example: nWaveMode is tp_nWaveMode.
These variables are hosted internally by TwistedPixel and thru the magic of the
scripting engine they are published for you to alter as necessary to achieve a
desired effect. Please note that not all variables are read/write -- there are
those variables specific to the geometry of the screen and so they cannot be
written to, such as tp_x, tp_y, tp_rad and tp_ang.
The following lists those preset variables you can write to and that are initialized in your PresetInitialize() method
|
Name |
Range |
Description |
| tp_nWaveMode | 0 ... 7 | Controls which of the 8 types of
built in waveform is to be drawn When providing custom waveforms you can set this value to -1 which will shut down the internal waveform display so as to give your custom waveform all the screen realestate. |
| tp_fWaveScale | > 0 | Amount to scale a waveform. (1) normal, (> 1) scale larger (< 1) scale smaller. |
| tp_fWaveAlpha | > 0 | Opacity level of the waveform. (0) transparent (1) opaque |
| tp_fWaveSmoothing | > 0 | Amount to apply a smoothing algorithm to the waveform. |
| tp_wave_x | 0 ... 1 | Position of the waveform: (0) far left edge of screen (0.5) center (1) far right |
| tp_wave_y | 0 ... 1 | Position of the waveform: (0) bottom of screen (0.5) center (1) top of the screen |
| tp_wave_r | 0 ... 1 | Red Component of the wave form |
| tp_wave_g | 0 ... 1 | Green Component of the wave form |
| tp_wave_b | 0 ... 1 | Blue Component of the wave form |
| tp_bWaveDots | 0 or 1 | When the value is non zero the
internal waveforms will be drawn as a series of dots. When the value is zero then the waveform will be drawn as a series of lines. |
| tp_bWaveThick | 0 or 1 | When the value is non zero the
waveform elements are drawn at twice there regular thickness. When the value is zero the waveform is drawn at its default thickness. |
| tp_bModWaveAlphaByVolume | 0 or 1 | When the value is non zero the
waveform elements opacity is dependent on the volume levels. When the value is zero the waveform elements opacity is independent of volume. |
| tp_fModWaveAlphaStart | 0 ... 1 | If tp_bModWaveAlphaByVolume is enabled then this value dictates the start alpha value to use |
| tp_fModWaveAlphaEnd | 0 ... 1 | If tp_bModWaveAlphaByVolume is enabled then this value dictates the end alpha value to use |
| tp_bAdditiveWaves | 0 or 1 | When the value is non zero the
waveform will be added to the screen's existing pixels. When the value is zero the waveform will be drawn over the existing pixel. |
| tp_bMaximizeWaveColor | 0 or 1 | Utilize the color component with the highest value to scale the other two color components thus providing a new color that is brighter and more saturated. |
| tp_bWaveOnlyLeft | 0 or 1 | Use just the left music channel in building the preset waveform |
| tp_bWaveOnlyRight | 0 or 1 | Use just the right music channel in building the preset waveform |
| tp_ob_size | 0 ... .5 | Thickness of the outer border drawn at the edges of the screen per each frame |
| tp_ob_r | 0 ... 1 | Red Component of the motion vector |
| tp_ob_g | 0 ... 1 | Green Component of the motion vector |
| tp_ob_b | 0 ... 1 | Blue Component of the motion vector |
| tp_ib_size | 0 ... .5 | Thickness of the inner border drawn at the edges of the screen per each frame |
| tp_ib_r | 0 ... 1 | Red Component of the motion vector |
| tp_ib_g | 0 ... 1 | Green Component of the motion vector |
| tp_ib_b | 0 ... 1 | Blue Component of the motion vector |
| tp_bMotionVectorsOn | 0 or 1 | Enable motion vectors -- this variable is no longer used. A zero or non zero value for tp_mv_a dictates motion vector visibilty. |
| tp_nMotionVectorsX | 0 ... 64 | The number of illuminated pixels to tile across the horizontal. A maximum of 64 is allowed. |
| tp_nMotionVectorsY | 0 ... 48 | The number of rows to apply
tp_nMotionVectorsX in the vertical direction. A maximum of 48 is allowed. |
| tp_mv_a | 0 ... 1 | The level of opacity applied to the motion vectors. |
| tp_mv_l | 0 ... 5 | Trail length of each motion vector (not currently implemeted). |
| tp_mv_dx | -1 ... 1 | Horizontal placement offset of the motion vectors. |
| tp_mv_dy | -1 ... 1 | Vertical placement offset of the motion vectors. |
| tp_mv_r | 0 ... 1 | Red Component of the motion vector |
| tp_mv_g | 0 ... 1 | Green Component of the motion vector |
| tp_mv_b | 0 ... 1 | Blue Component of the motion vector |
| tp_zoom | > 0 | Controls inward/outward motion. (0.9) zoom out 10% per frame (1.0) no zoom (1.1) zoom in 10% |
| tp_fZoomExponent | > 0 | Controls the curvature of the zoom (1) normal |
| tp_warp | > 0 | Controls the magnitude of the Milkdrop warping (0) none (1) normal (2)major warping etc. |
| tp_fWarpScale | > 0 | Amount to scale warp effect |
| tp_fWarpAnimSpeed | > 0 | Frequency or speed of warp effect |
| tp_rot | Controls the amount of rotation. (0) none (0.1) slightly right (0.1) slightly clockwise (0.1) CCW | |
| tp_sx | > 0 | Controls amount of constant horizontal stretch (0.99)shrink 1% (1)normal (1.01)stretch 1% |
| tp_sy | > 0 | Controls amount of constant vertical stretch (0.99)shrink 1% (1)normal (1.01)stretch 1% |
| tp_cx | 0 ... 1 | Controls where the center of rotation and stretching is in the horizontal. (0) left (0.5) center (1) right |
| tp_cy | 0 ... 1 | Controls where the center of rotation and stretching is in the vertical. (0) top (0.5) center (1) bottom |
| tp_dx | Controls amount of constant horizontal motion (-0.01) move left 1% per frame, (0) none, (0.01) move right 1% | |
| tp_dy | Controls amount of constant vertical motion (-0.01) move up 1% per frame, (0) none, (0.01) move down 1% | |
| tp_fDecay | 0 ... 1 | Controls the fade to black levels. (1) no fade (0.9) strong fade (0.98) recommended |
| tp_bDarkenCenter | 0 or 1 | Not implemented in TwistedPixel |
| tp_fGammaAdj |
> 0 |
Controls the level of brightness. (1) normal (2) double (3) triple, etc. |
| tp_fVideoEchoAlpha | 0 ... 1 | Controls the opacity of a second graphics layer where 1 is opaque and 0 is transparent. |
| tp_fVideoEchoZoom | > 0 | Controls the size of a second graphics layer when fVideoEchoAlpha is > 0 |
| tp_nVideoEchoOrientation | 0 ... 3 | Four positions to place the preset
frame as a second graphics layer to 'echo' the first. (0) normal (1) flip on x (2) flip on y (3) flip on both |
| tp_bTexWrap | 0 or 1 | For pixels that fall off the screen, they will be wrapped around the other side vs being clipped internally from the video buffer. Utilizing the wrapping of pixel values can produce some of the better presets. |
| tp_bRedBlueStereo | 0 or 1 | Get your movie time 3D glasses out...this enables 3D movie mode where the waveform will be displayed in the red and blue-green needed to give the effect of 3D. |
| tp_bInvert | 0 or 1 | Invert filter applied when value is 1 |
| tp_bBrighten | 0 or 1 | Brighten filter applied when value is 1 |
| tp_bDarken | 0 or 1 | Darken filter applied when value is 1 |
| tp_bSolarize | 0 or 1 | Solarize filter applied when value is 1 |
| tp_fDecay_Red | 0 ... 1 | red decay value to be multiplied by tp_fDecay and applied to red channel in processing decay |
| tp_fDecay_Green | 0 ... 1 | green decay value to be multiplied by tp_fDecay and applied to green channel in processing decay |
| tp_fDecay_Blue | 0 ... 1 | blue decay value to be multiplied by tp_fDecay and applied to blue channel in processing decay |
| tp_bEnableBeatDetection | 0 or 1 | Enable TwistedPixel internal beat detection algorithm |
| tp_bBeatDetected | 0 or 1 | A read-only per frame variable: (0) no beat has been detected, (1) a beat has been detected |
| tp_nBeatDetectionSensitivity | > 0 | A value used to set a threshold in determining if a given frame has a beat. Default value is 192. |
| tp_fDuration | > 0 | The number of seconds the preset is to run before TwistedPixel loads the next preset. |
| tp_fDesiredFPS | > 0 | The average number of frames per second the preset is to run at. You can change this value during the duration of the presets playing time to provide varying effects such as speeding up and slowing down in response to volume. |
| tp_NextPreset | String | A string value containing fully
qualified path/filename of the next preset to load This is useful if chaining multiple presets together in one overall presentation. If no value is provide then TwistedPixel will automatically select the next preset. |
| tp_volume | > 0 | A per frame volume amount as calculated by TwistedPixel |
| tp_TextureWidth | Read Only, Internal D3D texture size | |
| tp_TextureHeight | Read Only, Internal D3D texture size | |
| tp_MeshX, tp_MeshY | 8 ... 128 | Read/Write per frame internal
number of mesh points used in calculating per pixel values The higher the value the greater the image quality but the greater the performance penalty Modes generally set in PresetInitialize: (8,6) (16,12) (24,18) (32,24) (40,30) (48,36) (96,72) |
The following lists those preset variables that change per frame and are read only
|
Name |
Range |
Description |
| tp_time | > 0 | Retrieves the current time, in seconds, since TwistedPixel started running |
| tp_bass | > 0 | Retrieves the current amount of bass. 1 is normal; below ~0.7 is quiet; above ~1.3 is loud bass |
| tp_mid | > 0 | ... same as tp_bass but for mid range frequencies |
| tp_treb | > 0 | ... same as tp_bass but for high frequencies |
| tp_bass_att | > 0 | Retrieves an attenuated reading on the tp_bass, meaning that it is damped in time and doesn't change so rapidly. |
| tp_mid_att | > 0 | ... same as tp_bass_att but for tp_mid |
| tp_treb_att | > 0 | ... same as tp_treb_att but for tp_treb |
| tp_fps | > 0 | Average number of frames per second |
| tp_frame | > 0 | The frame count since TwistedPixel started |
| tp_progress | 0 ... 1 | Progress through the current preset If the preset was just loaded then this value is closer to 0 If the preset is about near ending then this value is closer to 1 |
|
Name |
Range |
Description |
| tp_x |
0 ... 1 |
Retrieves the x-position of the current pixel. At the left edge of the screen the value is 0, the middle value is 0.5 and the right edge value is 1 |
| tp_y |
0 ... 1 |
Retrieves the y-position of the current pixel. At the top of the screen the value is 0, the middle value is 0.5 and the bottom value is 1 |
| tp_rad |
0 ... 1 |
Retrieves the distance of the pixel from the center of the
screen. At the center of the screen the value is zero and the corners have a value of 1 The middle of the edges will be 0.707 which is half of the square root of 2 |
| tp_ang |
0 ... 6.28 |
Retrieves the angle of the current pixel with respect to the
center of the screen. If the point is to the right of the center then the value is zero. Above center is PI/2 or 1.57 To the left is PI or 3.14 Below center is 4.71 or PI*3/2 Below and to the right of the center the value will approach 6.28 or PI*2 *this is simply the arctangent of y over x precomputed |
From the color cycling section above we've
already seen an example of the type of code you can add to per frame processing.
Now lets change zoom and rotation a bit to see some per frame processing...
Keeping in mind that the sin function always returns a value between -1 and 1
the following line of code will cause the screen to zoom in and out over the
course of time
tp_zoom += 0.1 *
Math.Sin(tp_time);
The amount of zoom would oscillate between the values 0.9 and 1.1 over a period
of 6.28 seconds. If you want per frame tp_zoom to cycle every 2 seconds, you
would multiply tp_time by PI.
tp_zoom += 0.1 * Math.Sin(tp_time*3.14);
This is just a sampling of what can be completed within the body of
ProcessPerFrame method. Below is an example of what is Default.vbs which is a
generic VBScript preset used to demonstrate some of the code in this document.
'''
' Called per frame
'''
Sub ProcessPerFrame
' get the time -- make just one trip per-frame to do it in
fT = tp_time
tp_wave_r = tp_wave_r + .4 * ( .6 * Sin(0.933 * fT) + .4 * Sin(1.045 * fT) )
tp_wave_g = tp_wave_g + .4 * ( .6 * Sin(0.900 * fT) + .4 * Sin(0.956 * fT) )
tp_wave_b = tp_wave_b + .4 * ( .6 * Sin(0.910 * fT) + .4 * Sin(0.920 * fT) )
tp_zoom = tp_zoom + 0.1 * Sin(fT)
End Sub
Per Pixel Processing
Per frame processing is an activity that affects
the entire screen ... what if you want to change the rotation amount for a
specific area of the screen or you want to zoom just a portion of the screen,
this is where per pixel processing is used. Lets look at the following code:
'''
' Called per pixel
'''
Sub ProcessPerPixel
' apply per-pixel rotation
tp_rot = tp_rot + 0.16 * Sin(tp_time * -3.3 + tp_rad * 11)*(1.3 - tp_rad)
End Sub
The above code will use the radius relative to the x, y, position for this per pixel call and rotate that pixel a calculated amount that will be different from other positions on the screen. This approach makes for some fantastic presets.
It is now time to talk about the woes of per
pixel processing ... this is the one method call in your preset that in itself
occupies the most CPU time and can make or break performance in your preset.
Unlike Milkdrop and prior versions of TwistedPixel where an internal expression
parser could handle the expense of per pixel processing, this version of
TwistedPixel exposes the variables via a scripting engine that requires more
than one trip per use of a variable. This is the greatest deficit in per pixel
processing for this visualization. Given this issue you will want to limit the
number of trips to obtain TwistedPixel variable values. For example in the above
code the following changes should be made:
These small optimizations go a long away to producing more frames per second. The changes mentioned would appear:
'''
' Called per pixel
'''
Sub ProcessPerPixel
Dim fRad
fRad = tp_rad' apply per-pixel rotation
tp_rot = tp_rot + 0.16 * Sin(m_fT * -3.3 + fRad * 11)*(1.3 - fRad)
End Sub
Function Reference
Included with TwistedPixel are a series of
functions that will enhance your preset development. Some of these functions are
provided more for backward compatibility with Milkdrop -- it is preferred that
presets for TwistedPixel limit the use of these functions and instead replace
the use with code appropriate to that scripting languages environment. For
example: Milkdrop functions above, below and equal would be replaced with basic
if then else logic.
| DebugOut("some variable") | A useful method to send debug data to the screen while you develop your preset. Any string value can be used and can include tab and carriage return line feeds. |
| AddWaveformElement | Use in adding elements to a custom waveform -- see section on custom waveforms |
| WaveformLeft | Returns an array of left channel waveform data for the given frame. Use LBound and UBound to define array boundaries. See preset Spangler - FunColors.vbs for a demonstration. |
| WaveformRight | Returns an array of right channel waveform data for the given frame. Use LBound and UBound to define array boundaries. See preset Spangler - FunColors.vbs for a demonstration. |
| SpectrumLeft | Returns an array of left channel specturm data for the given frame. Use LBound and UBound to define array boundaries. |
| SpectrumRight | Returns an array of right channel specturm data for the given frame. Use LBound and UBound to define array boundaries. |
| Above(A,B) | Returns 1 if A > B, else 0 |
| acos(A) | Arc-cosine of A. Returns the angle, measured in radians, whose cosine is A |
| acosh | Same as acos() but for hyperbolic cosine |
| asin(A) | Returns the angle, measured in radians, whose sine is A |
| asinh | Same as asin() but for hyperbolic sine |
| atan(A) | Returns the angle, measured in radians, whose tangent is (A) |
| atan2(A,B) | Returns the Arc-tangent of (A/B), measured in radians |
| atanh(A) | Same as atan() but for hyperbolic tangent |
| band(A,B) | Boolean and, returns 1 if A and B are not equal to 0 |
| bor(A,B) | Boolean or, returns 1 if A or B are not equal to 0 |
| bnot(A) | Boolean not, returns 1 if A = = 0 or 0 if A != 0 |
| below(A,B) | Returns 1 if A < B, else 0 |
| besselJ0(A) | Bessel function of the first kind, order 0 |
| besselJ1(A) | Bessel function of the first kind, order 1 |
| besselJn(A,B) | Bessel function of the first kind, order n |
| besselY0(A) | Bessel function of the second kind, order 0 |
| besselY1(A) | Bessel function of the second kind, order 1 |
| besselYn(A) | Bessel function of the second kind, order n |
| deg2rad(A) | Convert A from radians to degrees |
| ceil(A) | Ceiling of A. Returns the smallest integer greater than A. Rounds up to the next higher integer |
| chgsign(A) | Reverses the sign of A; e.g. -A return A, A returns -A |
| copysign(A,B) | Copy the sign of B returning A; e.g. copysign(5, -2) return -5 and copysign(-5,2) returns 5 |
| cosh(A) | Same as cos() but for hyperbolic cosine |
| exp(A) | Exponential of A. Returns the value of e raised to the power A where e is the base of the natural logarithm, i.e. the non-repeating value approximately equal to 2.71828182846. More simply, the same as pow(e, A), where e is Euler's constant (2.7182..., the base of natural logarithms) |
| equal(A,B) | Returns 1 if A = B, else 0 |
| floor(A) | Floor of A. Returns the largest integer less than A. Rounds down to the next lower integer |
| frac(A) | Returns just fractional portion of the number |
| hypot(A,B) | Returns the length of the hypotenuse of a right triangle, given the length of the two sides A and B. This call is equivalent to sqrt(A*A+B*B) |
| iif(A,B,C) | If int(A) differs from 0, the return value of this function is B, else C. Only the parameter which needs to be evaluated is evaluated, the other parameter is skipped |
| int(A) | Returns integer value of A |
| log10(A) | Returns the log base 10 of the value A |
| max(A,B) | If A>B, the result is A, else B |
| min(A,B) | If A<B, the result is A, else B |
| pow(A,B) | Returns A to the power of B |
| rand(A) | Returns a random integer modulo A; e.g. rand(4) will return 0, 1, 2, or 3 |
| randf(A) | Returns a random float modulo A; e.g. randf(3.99) may return 0.233, 1.5, 2.22, or 3.8 |
| rad2deg(A) | Convert A from degrees to radians |
| round(A) | Rounds to integer portion with
rounding. If value negative then floor is used, if value is positive then ceil is used Similar to trunc, but different rounding behavior |
| sign(A) | Returns the sign of A or 0 < 0 = -1, > 0 = 1, 0 = 0 |
| sinh(A) | Same as sin() but for hyperbolic sine |
| pow2(A) | Returns the square of A |
| tanh(A) | Same as tan() but for hyperbolic tangent |
| trunc(A) | Truncates to integer portion
with rounding. If value negative then ceil is used, if value is positive then floor is used Similar to round, but different rounding behavior |
Custom Waveforms
With this release of TwistedPixel you can build
custom waveforms to be rendered to the screen during per frame processing.
A good example of this feature is in the FunColor.vbs preset. It is recommended
you review this preset after reading this section.
The method ProcessWaveform() is called when TwistedPixel is rendering waveforms
to the screen. In the body of this method you add your code to provide
TwistedPixel with custom waveforms on behalf of your preset.
A waveform is nothing more than a series of dots strung together at varying
positions across the screen -- if you have ever seen an oscilloscope or the tech
gadgets on one of the Star Trek series then you've no doubt come across a
waveform. To add your own waveform you use the TwistedPixel function AddWaveformElement
which sets one pixel element for the waveform you are drawing. Once you have
called AddWaveformElement and set all of the pixel elements for the waveform
exit the ProcessWaveForm function and TwistedPixel will apply the pixel elements
to the screen.
With this arrangement there is no limit on the number of custom waveforms you
can add to the screen ... of course the more you add the longer you delay the
display of the current frame. The process flow to expect when adding custom
waveforms is as follows:
TwistedPixel is notified a
frame is to be rendered
TwistedPixel obtains audio
information and makes it available to the preset
The presets ProcessPerFrame is
invoked and processed
Any internal waveforms and motion
vectors are processed
The presets ProcessWaveForm is
invoked
TwistedPixel
processes the waveform elements and renders them to the screen
The return
value from ProcessWaveForm is evaluated.
If the return value is True then TwistedPixel loops and again calls
ProcessWaveForm for the next custom Waveform
If the return value is False then TwistedPixel leaves the custom waveform
processing
TwistedPixel
leaves the waveform Processing
Any custom shapes are rendered
TwistedPixel presents the frame
The AddWaveformElement function requires the following parameters...
AddWaveformElement nOrdinal, xPos, yPos, red, green, blue, alpha
nOrdinal -- the index value of the pixel element. index values are 0 thru number of elements - 1.
xPos -- the x position on the screen in the range of 0 thru 1
yPos -- the y position on the screen in the range of 0 the 1
red -- the red component of the pixel element in the range of 0 thru 1
green -- the green component of the pixel element in the range of 0 thru 1
blue -- the blue component of the pixel element in the range of 0 thru 1
alpha -- the alpha component of the pixel element in the range of 0 thru 1
The body below is an example of plotting pixels on the screen from the sample vis Spangler - FunColors.vbs.
'''
' Request to build a custom waveform
'
' returning True to TwistedPixel is an indicator we want to build another
' returning False to TwistedPixel is an indicator we are done building waveforms for this m_fRedame
'''
Function ProcessWaveform
Dim i, nSample, nLength
Dim nIndex, fIndexfact, fVal
Dim fx, fy
fx = fy = CSng(0)
' only adding one waveform to the screen so return false after this call
ProcessWaveform = False
' Available byte array audio channels are WaveformRight, WaveformLeft, SpectrumRight & SpectrumLeft
' get the left audio channels of waveform data from WinAMP
Dim arrWaveformLeft
arrWaveformLeft = tp_WaveformLeft
' dampen the WinAMP waveform data to meet screen requirements
DampenWinAMPWaveForm arrWaveformLeft, m_fScale
' set preset params for display of this waveform
' *these preset vars are reset per-frame and so their setting here is specific to the addition of this waveform
tp_bMaximizeWaveColor=1
' iterate the number of elements we want to add to the waveform and set x, y positions
nLength = CInt(m_fWidth - 1)
For nSample = 0 to nLengthnIndex = CInt(nSample * m_fIndxFact)
' get the x,y positions to plot this point and scale it to [0..1]
fx = CSng(nSample * m_fScalefactHorz)
fy = .5 + (m_fScalefactVert * CSng(arrWaveformLeft(nIndex)))
' add the waveform element to the waveform
nIndex = CInt((m_nOffset + nSample) Mod NUMSAMPLES)
AddWaveformElement nSample, fx, fy, _
m_pRed(nIndex), _
m_pGreen(nIndex), _
m_pBlue(nIndex), _
m_fAlphaNext
End Function
Like Custom Waveforms you can also add your own
custom shapes to the screen as each frame is rendered. The following
processflow is the step by step of the order TwistedPixel uses to obtain and
display your preset's custom shapes.
TwistedPixel is notified a
frame is to be rendered
TwistedPixel obtains audio
information and makes it available to the preset
The presets ProcessPerFrame is
invoked and processed
Any internal waveforms and motion
vectors are processed
Any custom waveforms are rendered
The presets ProcessCustomShape is
invoked
TwistedPixel
processes the custom shape properties and renders the shape to the screen
The return
value from ProcessCustomShape is evaluated.
If the return value is True then TwistedPixel loops and again calls
ProcessCustomShape for the next custom shape
If the return value is False then TwistedPixel leaves the custom shapes
processing
TwistedPixel
leaves the shapes Processing
TwistedPixel completes shapes
processing
TwistedPixel presents the frame
There is no limit to the number of shapes you can add to the screen. A custom shape is an n-sided polygon with properties such as radius, position, inner, outer and border colors as well as a rotation value and a texture property. Unlike custom waveform where there is a specific function for adding waveform elements, it was decided to expose TwistedPixel variable properties pertaining to the scope of a shape's attributes. For each shape you want to render the following properties are required:
|
Name |
Range |
Description |
| tp_shape_enabled | 0 or 1 | Turn a specific shape on or
off for the frame's call to ProcessCustomShape This is useful when your code has many shapes and you want to toggle a specific shapes display based on the music |
| tp_shape_sides | > 0 | Number of sides the polygon shape requires |
| tp_shape_additive | 0 or 1 | On render of the shape, add the shapes pixels to the existing pixels on the screen, otherwise draw over the existing pixels |
| tp_shape_thickOutline | 0 or 1 | Draw a thick border vs. normal width border |
| tp_shape_textured | 0 or 1 | One of the better features -- when 1 the previous frame will be rendered on the shape. Fo a nice example of this use see preset Geiss-Feedback.vbs by Ryan Geiss. |
| tp_shape_x | 0 ... 1 | The center position in the horizontal of the shape where 0 is the left and 1 is the right |
| tp_shape_y | 0 ... 1 | The center position in the vertical of the shape where 0 is the top and 1 is the bottom |
| tp_shape_rad | > 0 | default radius of the shape |
| tp_shape_ang | 0 ... 6.28 | Angle of the shape relative to its origin |
| tp_shape_r | 0 ... 1 | The red component of the color towards the center of the shape |
| tp_shape_g | 0 ... 1 | The green component of the color towards the center of the shape |
| tp_shape_b | 0 ... 1 | The blue component of the color towards the center of the shape |
| tp_shape_a | 0 ... 1 | The alpha component for the color components towards the center of the shape |
| tp_shape_r2 | 0 ... 1 | The red component of the color towards the outer edge of the shape |
| tp_shape_g2 | 0 ... 1 | The green component of the color towards the outer edge of the shape |
| tp_shape_b2 | 0 ... 1 | The blue component of the color towards the outer edge of the shape |
| tp_shape_a2 | 0 ... 1 | The alpha component for the color components towards the outer edge of the shape |
| tp_shape_border_r | 0 ... 1 | The red component of the border color |
| tp_shape_border_g | 0 ... 1 | The green component of the border color |
| tp_shape_border_b | 0 ... 1 | The blue component of the border color |
| tp_shape_border_a | 0 ... 1 | The alpha component of the border color |
The body below is an example of drawing a shape per frame on the screen...from
spangler - shape test.vbs
For a very good example of using multiple shapes as well as textures, review
Geiss-Feedback.vbs
'''
' called during per-frame processing
' returning true will direct TwistedPixel to invoke this function again to get the next Custom Shape
' returning false will indicate that no more custom shapes are to be rendered by TwistedPixel
Function ProcessCustomShapeProcessCustomShape = False
' set custom shape preset vars
tp_shape_enabled = 1
tp_shape_sides=4
tp_shape_additive=0
tp_shape_thickOutline=0
tp_shape_textured=1
tp_shape_x=0.500000
tp_shape_y=0.500000
tp_shape_rad=1.000000
tp_shape_ang=0.0000
tp_shape_r=1.000000
tp_shape_g=0.000000
tp_shape_b=0.000000
tp_shape_a=0.500000
tp_shape_r2=0.000000
tp_shape_g2=0.000000
tp_shape_b2=1.000000
tp_shape_a2=0.500000
tp_shape_border_r=1.000000
tp_shape_border_g=1.000000
tp_shape_border_b=0.000000
tp_shape_border_a=0.5000000
' apply some rotation and play around with the radius
tp_shape_ang = fT * (0.3 + 0.1 * sin(fT))
tp_shape_rad = tp_shape_rad + 0.1 * Sin(fT)End Function
For those preset authors who might have millions of presets (OK well maybe a few thousand) I have written a small utility that will convert your Mildrop Presets to either VBScript or JavaScript. You can find the MD2TP.exe utility in the Plugins\TwistedPixel SDK folder -- upon running the utility you will want to select either single file mode or batch mode. When in single file mode you will convert one file at a time where both the source and destination scripts are displayed in the conversion windows. When in batch mode you are prompted to select a source folder and a destination folder. All the Milkdrop presets found in the source folder will be converted to the scripting language you selected and saved in the destination folder.
The conversion utility is not perfect and will sometimes fail to provide a conversion that is 1:1 with the original Milkdrop preset. For this reason I added the I hotkey to TwistedPixel that you can use to flag a preset as incomplete. When a preset is flagged as incomplete its filename will be prefixed with 'incomplete.conversion.' so that you can recognize it at a later time and open it for edit. I generally convert an entire folder of Milkdrop presets to an empty Plugins\TwistedPixel folder. I then run WinAMP and start loading presets for review using the hotkey n to advance to the next preset. When a preset looks to be faulty or displays a Script Engine runtime error I'll press the hotkey I and move on to the next preset.
This utility is still under development so please contact me if you find any issues with it or would like to see new features added.
*one note -- the current release of the conversion utility does not recognize custom shapes or custom waveforms.
Debugging your presets with Microsoft's Script Editor
How To Debug your scripts, including setting break
points, adding variables to a watch window etc.
First off I must warn that setting up Microsoft Script Debugging is completely
hit or miss -- I have tested the following on
3 machines and found success on two of them...oops just tested again, now its
just one.
The recipe I used for setting up an environment for script debugging is the
following:
Requirements:
-It seem version of WinAMP prior to release 2.91 will not work so ensure you
have 2.91 or better
-For debugging script I use MSE, Microsoft Script Editor, version 10 which is
provided as part of OfficeXP
-Download and install the latest script engines from Microsoft; currently
release 5.6 and available for download
at http://msdn.microsoft.com/library/default.asp?url=/downloads/list/webdev.asp
-Download and install the current release of the Microsoft Script Debugger
runtime files
for NT 4.0, 2000 and XP -- file: scd10en.exe
http://www.microsoft.com/downloads/details.aspx?FamilyId=2F465BE0-94FD-4569-B3C4-DFFDF19CCD99&displaylang=en
for 98 and Me -- file: ie401dbg.exe
http://www.microsoft.com/downloads/details.aspx?FamilyId=E606E71F-BA7F-471E-A57D-F2216D81EC3D&displaylang=en
How to start debugging your presets
. Open the preset you want to debug using any editor, I use MSE, and change the
preset variable tp_fDuration to a value of -1
in method PresetInitialize. This will eliminate any transitions to another
preset while you are debugging.
. Run WinAMP and open TwistedPixel using Shit+Ctrl+k and load the preset you
want to debug by pressing the O key for Open
Preset ... do start the song just yet.
. Make sure that the TwistedPixel window has focus and is in the foreground (if
your not sure then just click on the title
bar) then lauch the Script Debugger (I keep a shortcut to MSE7.exe on my
desktop)
. From MSE click on menu items [Debug] and then [Processes] A window opens
showing all the current processes available,
scroll down until you see the process WinAMP. The title next to WinAMP should be
TwistedPixel, if it is not then use your
Alt-Tab key until you bring the TwistedPixel window to the foreground. Click on
the TwistedPixel title bar and return to the
MSE Processes Window where you will need to click the refresh button to update
the listing of processes and titles. Once you
find the WinAMP process with TwistedPixel title click on it and then press the
Attach Button. A small window will be
presented to you where you have a check box option titled Script, leave this
value checked. Below the check box in the pane
title 'The following programs will be debugged' should be listed one entry
titled 'Script' if this value is missing then you
will not be establish a debug session and will have to join the millions of us
who have tried and failed repeatedly before
lucking out.
Press the Select All button and Followed by the OK button. You will have
returned to the Processes window where you'll now
press the 'break' button. This instructs the editor to break on the fist line of
code that is to execute when you start a
song. Close the Processes window and Alt-Tab to the TwistedPixel window and
press X to start a song.
. Upon the music starting you should be thrown into an MSE Debug session with
the source code to your preset displayed to you
. At this point you will use the tools of the debugger to add variables to watch
windows, set break points etc. I usually set
a break point on ProcessPerFrame and then press f5 to continue debugging.
. If at anytime the code suddenly disappears it only means the preset unloaded
and although WinAMP is still running with
TwistedPixel there is no preset loaded for debugging ... no worry just go to the
[Debug] menu and click on [Break All] this
will immediately break execution in the current preset running of which you can
now set the break points, watches etc.
That is about it -- as I stated earlier there is no guarantee this will work on
your machine, all I can do is wish you luck.
If you are fortunate enough to get setup with debugging then you will find
authoring your presets a much easier task when you
have a debugger handy.
Lots of Luck, if you find other approaches to getting the script debugger
working then please let me know so that I can post
for others to try.